##Creating Log price
data_final$logprice <- log(data_final$price)
#summary(data_final$logprice) #has inf values because of log(0)
data_final$logprice[which(!is.finite(data_final$logprice))] <- 0 #add 0 where the value is inf
#repeat train split as the data in final data was modified
# Create a train-split sets
set.seed(123)
data_split <- initial_split(data_final, prop = 0.7)
data_train <- training(data_split)
data_test <- testing(data_split)
# Generate 10-fold CV sets
set.seed(321)
data_folds <- vfold_cv(data_train, v = 10)
data_folds
# 10-fold cross-validation
Linear Lasso Reqularized Regression Model
In this section, regularized regression model will be speficied and trained. Lasso penalty is chosen to simultaneously perform subset selection. Therefore, mixture is set to 1 in the model specification.
Model specification
Specification of lasso-regularized logistic regression model, where the penalty parameter will be tuned:
lasso_linreg <- linear_reg(penalty = tune(), mixture = 1) %>%
set_engine("glmnet")
#check that model specified correctly:
lasso_linreg %>% translate()
Linear Regression Model Specification (regression)
Main Arguments:
penalty = tune()
mixture = 1
Computational engine: glmnet
Model fit template:
glmnet::glmnet(x = missing_arg(), y = missing_arg(), weights = missing_arg(),
alpha = 1, family = "gaussian")
Preprocessing recipe
In this section, the recipe is formulated. All the variables included in the final dataset are included in the recipe, in order to perform the subset selection through the lasso penalty. As the property type and bed type have some categories with just a few observations, the categories that include less than 1% of the total number of observations are combined to “other” category to avoid sparse data. Additionally, dummies are created for all of the nominal variables. Lastly, all the variables are normalized.
did not include: availability_90 and availability_365, and the specific variables for calculated_host_listings_count_
lasso_recipe <- recipe(logprice ~ property_type + room_type + accommodates + bathrooms + bedrooms + beds +
bed_type + host_response_time + host_listings_count + host_identity_verified +
neighbourhood_cleansed + guests_included + extra_people + minimum_nights +
maximum_nights + availability_30 + instant_bookable + cancellation_policy +
require_guest_profile_picture + require_guest_phone_verification +
calculated_host_listings_count + wifi + pool + hot_tub + host_email + host_phone +
host_facebook + host_government_id + host_years_active,
data = data_train) %>%
step_other(property_type, bed_type, threshold = 0.01, other = "other values") %>%
step_dummy(all_nominal(), -all_outcomes()) %>%
step_normalize(all_predictors(), -all_outcomes())
lasso_recipe
Data Recipe
Inputs:
Operations:
Collapsing factor levels for property_type, bed_type
Dummy variables from all_nominal(), -all_outcomes()
Centering and scaling for all_predictors(), -all_outcomes()
Testing that this works properly:
data_baked <- lasso_recipe %>% prep(data_train) %>% bake(data_train)
head(data_baked)
Create Lasso Workflow
lasso_wf <- workflow() %>%
add_recipe(lasso_recipe) %>%
add_model(lasso_linreg)
lasso_wf
== Workflow ====================================================================
[3mPreprocessor:[23m Recipe
[3mModel:[23m linear_reg()
-- Preprocessor ----------------------------------------------------------------
3 Recipe Steps
* step_other()
* step_dummy()
* step_normalize()
-- Model -----------------------------------------------------------------------
Linear Regression Model Specification (regression)
Main Arguments:
penalty = tune()
mixture = 1
Computational engine: glmnet
Tuning grids
Next, the \(\lambda\) parameter of the lasso model will be tuned. For that purpose, a tuning grid is specified.
grid_lasso <- tibble(penalty = 10^(seq(from = -5, to = 1, length.out = 70)))
Tuning lasso-penalized linear regression
10-k-cross-validation is used to tune the lasso-penalized linear regression:
lasso_tune <- lasso_wf %>%
tune_grid(resamples = data_folds,
grid = grid_lasso,
metrics = metric_set(mae, rmse, rsq_trad))
Plot the metrics against lambda:
lasso_tune_metrics <- lasso_tune %>%
collect_metrics()
lasso_tune_metrics %>% filter(.metric == "rmse") %>%
ggplot(aes(x = penalty, y = mean,
ymin = mean - std_err, ymax = mean + std_err)) +
geom_linerange(alpha = 0.5) +
geom_point() +
scale_x_log10() +
labs(y = "RMSE", x = expression(lambda))

lasso_tune_metrics %>% filter(.metric == "rsq_trad") %>%
ggplot(aes(x = penalty, y = mean,
ymin = mean - std_err, ymax = mean + std_err)) +
geom_linerange(alpha = 0.5) +
geom_point() +
scale_x_log10() +
labs(y = "rsq_trad", x = expression(lambda))

lasso_tune_metrics %>% filter(.metric == "mae") %>%
ggplot(aes(x = penalty, y = mean,
ymin = mean - std_err, ymax = mean + std_err)) +
geom_linerange(alpha = 0.5) +
geom_point() +
scale_x_log10() +
labs(y = "mae", x = expression(lambda))

Next, the best model with the best value of Lambda is selected. It can be seen that as the RMSE is more sensitive for large residuals, the std errors of this metrics are larger compared to the standard errors of mean absolute error (mae). Therefore, mean absolute error is used to select the best model.
lasso_tune %>% show_best("mae")
The best model is selected using the one standard error rule, where the simplest model that has mae inside one standard error from the absolute best model is chosen to avoid overfitting.
lasso_1se_model <- select_by_one_std_err(lasso_tune, metric = "mae", desc(penalty))
lasso_1se_model
As can be seen, the best model has penalty parameter of 0.007.
Finalize the workflow:
lasso_wf_tuned <-
lasso_wf %>%
finalize_workflow(lasso_1se_model)
lasso_wf_tuned
== Workflow ====================================================================
[3mPreprocessor:[23m Recipe
[3mModel:[23m linear_reg()
-- Preprocessor ----------------------------------------------------------------
3 Recipe Steps
* step_other()
* step_dummy()
* step_normalize()
-- Model -----------------------------------------------------------------------
Linear Regression Model Specification (regression)
Main Arguments:
penalty = 0.00740568469226243
mixture = 1
Computational engine: glmnet
lasso_last_fit <- lasso_wf_tuned %>%
last_fit(data_split, metrics = metric_set(mae, rmse, rsq_trad))
The performance on the test set for this model is:
lasso_test_metrics <- lasso_last_fit %>% collect_metrics()
lasso_test_metrics
To assess the importance of the predictor variables, model parameter estimates are calulated below:
lasso_wf_tuned %>% fit(data_train) %>% pull_workflow_fit() %>% tidy()
NA
As lasso performs subset selection automatically, some variables have coefficient of zero.
LS0tDQp0aXRsZTogIlIgY29kZSBmb3IgUmVndWxhcml6ZWQgUmVncmVzc2lvbiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjQ3JlYXRpbmcgTG9nIHByaWNlDQoNCmBgYHtyfQ0KZGF0YV9maW5hbCRsb2dwcmljZSA8LSBsb2coZGF0YV9maW5hbCRwcmljZSkNCg0KI3N1bW1hcnkoZGF0YV9maW5hbCRsb2dwcmljZSkgI2hhcyBpbmYgdmFsdWVzIGJlY2F1c2Ugb2YgbG9nKDApDQpkYXRhX2ZpbmFsJGxvZ3ByaWNlW3doaWNoKCFpcy5maW5pdGUoZGF0YV9maW5hbCRsb2dwcmljZSkpXSA8LSAwICNhZGQgMCB3aGVyZSB0aGUgdmFsdWUgaXMgaW5mDQoNCiNyZXBlYXQgdHJhaW4gc3BsaXQgYXMgdGhlIGRhdGEgaW4gZmluYWwgZGF0YSB3YXMgbW9kaWZpZWQNCiMgQ3JlYXRlIGEgdHJhaW4tc3BsaXQgc2V0cyANCnNldC5zZWVkKDEyMykNCmRhdGFfc3BsaXQgPC0gaW5pdGlhbF9zcGxpdChkYXRhX2ZpbmFsLCBwcm9wID0gMC43KQ0KZGF0YV90cmFpbiA8LSB0cmFpbmluZyhkYXRhX3NwbGl0KQ0KZGF0YV90ZXN0IDwtIHRlc3RpbmcoZGF0YV9zcGxpdCkNCg0KIyBHZW5lcmF0ZSAxMC1mb2xkIENWIHNldHMNCnNldC5zZWVkKDMyMSkNCmRhdGFfZm9sZHMgPC0gdmZvbGRfY3YoZGF0YV90cmFpbiwgdiA9IDEwKQ0KZGF0YV9mb2xkcw0KYGBgDQoNCg0KDQojIyBMaW5lYXIgTGFzc28gUmVxdWxhcml6ZWQgUmVncmVzc2lvbiBNb2RlbA0KDQpJbiB0aGlzIHNlY3Rpb24sIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gbW9kZWwgd2lsbCBiZSBzcGVmaWNpZWQgYW5kIHRyYWluZWQuIExhc3NvIHBlbmFsdHkgaXMgY2hvc2VuIHRvIHNpbXVsdGFuZW91c2x5IHBlcmZvcm0gc3Vic2V0IHNlbGVjdGlvbi4gDQpUaGVyZWZvcmUsIG1peHR1cmUgaXMgc2V0IHRvIDEgaW4gdGhlIG1vZGVsIHNwZWNpZmljYXRpb24uIA0KDQojIyBNb2RlbCBzcGVjaWZpY2F0aW9uDQpTcGVjaWZpY2F0aW9uIG9mIGxhc3NvLXJlZ3VsYXJpemVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwsIHdoZXJlIHRoZSBwZW5hbHR5IHBhcmFtZXRlciB3aWxsIGJlIHR1bmVkOg0KYGBge3J9DQpsYXNzb19saW5yZWcgPC0gbGluZWFyX3JlZyhwZW5hbHR5ID0gdHVuZSgpLCBtaXh0dXJlID0gMSkgJT4lIA0KICBzZXRfZW5naW5lKCJnbG1uZXQiKQ0KDQojY2hlY2sgdGhhdCBtb2RlbCBzcGVjaWZpZWQgY29ycmVjdGx5Og0KbGFzc29fbGlucmVnICU+JSB0cmFuc2xhdGUoKQ0KYGBgDQoNCiMjIFByZXByb2Nlc3NpbmcgcmVjaXBlDQoNCkluIHRoaXMgc2VjdGlvbiwgdGhlIHJlY2lwZSBpcyBmb3JtdWxhdGVkLiBBbGwgdGhlIHZhcmlhYmxlcyBpbmNsdWRlZCBpbiB0aGUgZmluYWwgZGF0YXNldCBhcmUgaW5jbHVkZWQgaW4gdGhlIHJlY2lwZSwgaW4gb3JkZXIgdG8gcGVyZm9ybSB0aGUgc3Vic2V0IHNlbGVjdGlvbiB0aHJvdWdoIHRoZSBsYXNzbyBwZW5hbHR5LiBBcyB0aGUgcHJvcGVydHkgdHlwZSBhbmQgYmVkIHR5cGUgaGF2ZSBzb21lIGNhdGVnb3JpZXMgd2l0aCBqdXN0IGEgZmV3IG9ic2VydmF0aW9ucywgdGhlIGNhdGVnb3JpZXMgdGhhdCBpbmNsdWRlIGxlc3MgdGhhbiAxJSBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBhcmUgY29tYmluZWQgdG8gIm90aGVyIiBjYXRlZ29yeSB0byBhdm9pZCBzcGFyc2UgZGF0YS4gQWRkaXRpb25hbGx5LCBkdW1taWVzIGFyZSBjcmVhdGVkIGZvciBhbGwgb2YgdGhlIG5vbWluYWwgdmFyaWFibGVzLiBMYXN0bHksIGFsbCB0aGUgdmFyaWFibGVzIGFyZSBub3JtYWxpemVkLg0KDQpkaWQgbm90IGluY2x1ZGU6IGF2YWlsYWJpbGl0eV85MCBhbmQgYXZhaWxhYmlsaXR5XzM2NSwgYW5kIHRoZSBzcGVjaWZpYyB2YXJpYWJsZXMgZm9yIGNhbGN1bGF0ZWRfaG9zdF9saXN0aW5nc19jb3VudF8NCg0KYGBge3J9DQpsYXNzb19yZWNpcGUgPC0gIHJlY2lwZShsb2dwcmljZSB+IHByb3BlcnR5X3R5cGUgKyByb29tX3R5cGUgKyBhY2NvbW1vZGF0ZXMgKyBiYXRocm9vbXMgKyBiZWRyb29tcyArIGJlZHMgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICBiZWRfdHlwZSAgKyBob3N0X3Jlc3BvbnNlX3RpbWUgKyBob3N0X2xpc3RpbmdzX2NvdW50ICsgaG9zdF9pZGVudGl0eV92ZXJpZmllZCArDQogICAgICAgICAgICAgICAgICAgICAgICAgIG5laWdoYm91cmhvb2RfY2xlYW5zZWQgKyBndWVzdHNfaW5jbHVkZWQgKyBleHRyYV9wZW9wbGUgKyBtaW5pbXVtX25pZ2h0cyArIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhpbXVtX25pZ2h0cyArIGF2YWlsYWJpbGl0eV8zMCArIGluc3RhbnRfYm9va2FibGUgKyBjYW5jZWxsYXRpb25fcG9saWN5ICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmVxdWlyZV9ndWVzdF9wcm9maWxlX3BpY3R1cmUgKyByZXF1aXJlX2d1ZXN0X3Bob25lX3ZlcmlmaWNhdGlvbiArIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjYWxjdWxhdGVkX2hvc3RfbGlzdGluZ3NfY291bnQgKyB3aWZpICsgcG9vbCArIGhvdF90dWIgKyBob3N0X2VtYWlsICsgaG9zdF9waG9uZSArDQogICAgICAgICAgICAgICAgICAgICAgICAgIGhvc3RfZmFjZWJvb2sgKyBob3N0X2dvdmVybm1lbnRfaWQgKyBob3N0X3llYXJzX2FjdGl2ZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICBzdGVwX290aGVyKHByb3BlcnR5X3R5cGUsIGJlZF90eXBlLCAgdGhyZXNob2xkID0gMC4wMSwgb3RoZXIgPSAib3RoZXIgdmFsdWVzIikgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgc3RlcF9kdW1teShhbGxfbm9taW5hbCgpLCAtYWxsX291dGNvbWVzKCkpICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgc3RlcF9ub3JtYWxpemUoYWxsX3ByZWRpY3RvcnMoKSwgLWFsbF9vdXRjb21lcygpKQ0KDQpsYXNzb19yZWNpcGUNCmBgYA0KDQpUZXN0aW5nIHRoYXQgdGhpcyB3b3JrcyBwcm9wZXJseToNCmBgYHtyfQ0KZGF0YV9iYWtlZCA8LSBsYXNzb19yZWNpcGUgJT4lIHByZXAoZGF0YV90cmFpbikgJT4lIGJha2UoZGF0YV90cmFpbikNCmhlYWQoZGF0YV9iYWtlZCkNCmBgYA0KDQoNCg0KDQojIyBDcmVhdGUgTGFzc28gV29ya2Zsb3cNCmBgYHtyfQ0KbGFzc29fd2YgPC0gd29ya2Zsb3coKSAlPiUgDQogIGFkZF9yZWNpcGUobGFzc29fcmVjaXBlKSAlPiUgDQogIGFkZF9tb2RlbChsYXNzb19saW5yZWcpDQpsYXNzb193Zg0KYGBgDQoNCiMjIFR1bmluZyBncmlkcw0KTmV4dCwgdGhlICRcbGFtYmRhJCBwYXJhbWV0ZXIgb2YgdGhlIGxhc3NvIG1vZGVsIHdpbGwgYmUgdHVuZWQuIEZvciB0aGF0IHB1cnBvc2UsIGEgdHVuaW5nIGdyaWQgaXMgc3BlY2lmaWVkLiANCmBgYHtyfQ0KZ3JpZF9sYXNzbyA8LSB0aWJibGUocGVuYWx0eSA9IDEwXihzZXEoZnJvbSA9IC01LCB0byA9IDEsIGxlbmd0aC5vdXQgPSA3MCkpKQ0KYGBgDQoNCiMjIFR1bmluZyBsYXNzby1wZW5hbGl6ZWQgbGluZWFyIHJlZ3Jlc3Npb24NCg0KMTAtay1jcm9zcy12YWxpZGF0aW9uIGlzIHVzZWQgdG8gdHVuZSB0aGUgbGFzc28tcGVuYWxpemVkIGxpbmVhciByZWdyZXNzaW9uOg0KYGBge3J9DQpsYXNzb190dW5lIDwtIGxhc3NvX3dmICU+JSANCiAgdHVuZV9ncmlkKHJlc2FtcGxlcyA9IGRhdGFfZm9sZHMsIA0KICAgICAgICAgICAgZ3JpZCA9IGdyaWRfbGFzc28sDQogICAgICAgICAgICBtZXRyaWNzID0gbWV0cmljX3NldChtYWUsIHJtc2UsIHJzcV90cmFkKSkNCmBgYA0KDQpQbG90IHRoZSBtZXRyaWNzIGFnYWluc3QgbGFtYmRhOg0KYGBge3J9DQpsYXNzb190dW5lX21ldHJpY3MgPC0gbGFzc29fdHVuZSAlPiUgDQogIGNvbGxlY3RfbWV0cmljcygpDQpsYXNzb190dW5lX21ldHJpY3MgJT4lIGZpbHRlcigubWV0cmljID09ICJybXNlIikgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBwZW5hbHR5LCB5ID0gbWVhbiwgDQogICAgICAgICAgICAgeW1pbiA9IG1lYW4gLSBzdGRfZXJyLCB5bWF4ID0gbWVhbiArIHN0ZF9lcnIpKSArIA0KICBnZW9tX2xpbmVyYW5nZShhbHBoYSA9IDAuNSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIHNjYWxlX3hfbG9nMTAoKSArIA0KICBsYWJzKHkgPSAiUk1TRSIsIHggPSBleHByZXNzaW9uKGxhbWJkYSkpDQoNCmxhc3NvX3R1bmVfbWV0cmljcyAlPiUgZmlsdGVyKC5tZXRyaWMgPT0gInJzcV90cmFkIikgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBwZW5hbHR5LCB5ID0gbWVhbiwgDQogICAgICAgICAgICAgeW1pbiA9IG1lYW4gLSBzdGRfZXJyLCB5bWF4ID0gbWVhbiArIHN0ZF9lcnIpKSArIA0KICBnZW9tX2xpbmVyYW5nZShhbHBoYSA9IDAuNSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIHNjYWxlX3hfbG9nMTAoKSArIA0KICBsYWJzKHkgPSAicnNxX3RyYWQiLCB4ID0gZXhwcmVzc2lvbihsYW1iZGEpKQ0KDQpsYXNzb190dW5lX21ldHJpY3MgJT4lIGZpbHRlcigubWV0cmljID09ICJtYWUiKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHBlbmFsdHksIHkgPSBtZWFuLCANCiAgICAgICAgICAgICB5bWluID0gbWVhbiAtIHN0ZF9lcnIsIHltYXggPSBtZWFuICsgc3RkX2VycikpICsgDQogIGdlb21fbGluZXJhbmdlKGFscGhhID0gMC41KSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgc2NhbGVfeF9sb2cxMCgpICsgDQogIGxhYnMoeSA9ICJtYWUiLCB4ID0gZXhwcmVzc2lvbihsYW1iZGEpKQ0KDQpgYGANCg0KDQpOZXh0LCB0aGUgYmVzdCBtb2RlbCB3aXRoIHRoZSBiZXN0IHZhbHVlIG9mIExhbWJkYSBpcyBzZWxlY3RlZC4gSXQgY2FuIGJlIHNlZW4gdGhhdCBhcyB0aGUgUk1TRSBpcyBtb3JlIHNlbnNpdGl2ZSBmb3IgbGFyZ2UgcmVzaWR1YWxzLCB0aGUgc3RkIGVycm9ycyBvZiB0aGlzIG1ldHJpY3MgYXJlIGxhcmdlciBjb21wYXJlZCB0byB0aGUgc3RhbmRhcmQgZXJyb3JzIG9mIG1lYW4gYWJzb2x1dGUgZXJyb3IgKG1hZSkuIFRoZXJlZm9yZSwgbWVhbiBhYnNvbHV0ZSBlcnJvciBpcyB1c2VkIHRvIHNlbGVjdCB0aGUgYmVzdCBtb2RlbC4gDQoNCmBgYHtyfQ0KbGFzc29fdHVuZSAlPiUgc2hvd19iZXN0KCJtYWUiKQ0KYGBgDQoNClRoZSBiZXN0IG1vZGVsIGlzIHNlbGVjdGVkIHVzaW5nIHRoZSBvbmUgc3RhbmRhcmQgZXJyb3IgcnVsZSwgd2hlcmUgdGhlIHNpbXBsZXN0IG1vZGVsIHRoYXQgaGFzIG1hZSBpbnNpZGUgb25lIHN0YW5kYXJkIGVycm9yIGZyb20gdGhlIGFic29sdXRlIGJlc3QgbW9kZWwgaXMgY2hvc2VuIHRvIGF2b2lkIG92ZXJmaXR0aW5nLg0KYGBge3J9DQpsYXNzb18xc2VfbW9kZWwgPC0gc2VsZWN0X2J5X29uZV9zdGRfZXJyKGxhc3NvX3R1bmUsIG1ldHJpYyA9ICJtYWUiLCBkZXNjKHBlbmFsdHkpKQ0KbGFzc29fMXNlX21vZGVsDQpgYGANCkFzIGNhbiBiZSBzZWVuLCB0aGUgYmVzdCBtb2RlbCBoYXMgcGVuYWx0eSBwYXJhbWV0ZXIgb2YgMC4wMDcuDQoNCg0KRmluYWxpemUgdGhlIHdvcmtmbG93Og0KYGBge3J9DQpsYXNzb193Zl90dW5lZCA8LSANCiAgbGFzc29fd2YgJT4lIA0KICBmaW5hbGl6ZV93b3JrZmxvdyhsYXNzb18xc2VfbW9kZWwpDQpsYXNzb193Zl90dW5lZA0KYGBgDQoNCg0KYGBge3J9DQpsYXNzb19sYXN0X2ZpdCA8LSBsYXNzb193Zl90dW5lZCAlPiUgDQogIGxhc3RfZml0KGRhdGFfc3BsaXQsIG1ldHJpY3MgPSBtZXRyaWNfc2V0KG1hZSwgcm1zZSwgcnNxX3RyYWQpKQ0KYGBgDQoNClRoZSBwZXJmb3JtYW5jZSBvbiB0aGUgdGVzdCBzZXQgZm9yIHRoaXMgbW9kZWwgaXM6DQpgYGB7cn0NCmxhc3NvX3Rlc3RfbWV0cmljcyA8LSBsYXNzb19sYXN0X2ZpdCAlPiUgY29sbGVjdF9tZXRyaWNzKCkNCmxhc3NvX3Rlc3RfbWV0cmljcw0KYGBgDQoNCg0KVG8gYXNzZXNzIHRoZSBpbXBvcnRhbmNlIG9mIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzLCBtb2RlbCBwYXJhbWV0ZXIgZXN0aW1hdGVzIGFyZSBjYWx1bGF0ZWQgYmVsb3c6DQpgYGB7cn0NCmxhc3NvX3dmX3R1bmVkICU+JSBmaXQoZGF0YV90cmFpbikgJT4lIHB1bGxfd29ya2Zsb3dfZml0KCkgJT4lIHRpZHkoKSANCg0KYGBgDQoNCkFzIGxhc3NvIHBlcmZvcm1zIHN1YnNldCBzZWxlY3Rpb24gYXV0b21hdGljYWxseSwgc29tZSB2YXJpYWJsZXMgaGF2ZSBjb2VmZmljaWVudCBvZiB6ZXJvLiANCg0KDQoNCg0KDQoNCg==